package com.gitee.l0km.codegen.thrift;

import static com.gitee.l0km.com4j.basex.bean.jdk.BeanPropertySupportImpl.BEAN_SUPPORT;
import static com.gitee.l0km.xthrift.base.BaseThriftUtils.*;
import static com.google.common.base.Preconditions.*;
import static com.gitee.l0km.com4j.basex.reflection.generics.TypeResolution.resolveType;
import static com.gitee.l0km.common.spring.core.BridgeMethodResolver.findBridgedMethod;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.codegen.base.AbstractSchema;
import com.gitee.l0km.codegen.base.Method;
import com.gitee.l0km.codegen.base.Method.Parameter;
import com.gitee.l0km.com4j.basex.reflection.ParameterizedTypeImpl;
import com.gitee.l0km.xthrift.base.BaseThriftUtils;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;

import okio.ByteString;

/**
 * @author guyadong
 *
 */
public class TypeHelper implements ThriftConstants {
	private static final Logger logger = LoggerFactory.getLogger(TypeHelper.class);
	private final Set<Class<?>> knownTypes = Sets.newHashSet();
	private final Set<Class<?>> referTypes = Sets.newHashSet();
	private final Map<Class<?>,ThriftStructDecorator> decorateTypes = Maps.newHashMap();
	private final List<ThriftStructDecorator> thriftTypes = Lists.newLinkedList();
	private final AbstractSchema parent;
    private final static ThreadLocal<Deque<Class<?>>> tlsStack = new ThreadLocal<Deque<Class<?>>>()
    {
        @Override
        protected Deque<Class<?>> initialValue()
        {
            return new ArrayDeque<>();
        }
    };
    /**
     * 用于外部监控所有的类型
     */
    public static final ThreadLocal<Predicate<Type>> VERIFYTYPE_MONITOR = new ThreadLocal<Predicate<Type>>();
	public TypeHelper(AbstractSchema parent) {
		this.parent = checkNotNull(parent,"parent is null");
		knownTypes.addAll(BaseThriftUtils.THRIFT_BUILTIN_KNOWNTYPES);
		knownTypes.addAll(BaseThriftUtils.CAST_TYPES.keySet());
	}
	public final boolean verifyType(Type type){
		if(null != VERIFYTYPE_MONITOR.get()){
			VERIFYTYPE_MONITOR.get().apply(type);
		}
		if (knownTypes.contains(type)) {
			return true;
		}
		if( type instanceof ParameterizedType){
			ParameterizedType paramType = (ParameterizedType)type;
			Type rawType = paramType.getRawType();
			Type[] typeArgs = paramType.getActualTypeArguments();
			if(rawType == Map.class){
				return verifyType(typeArgs[0]) && verifyType(typeArgs[1]);
			}else if (rawType == List.class){
				return verifyType(typeArgs[0]);
			}else if(rawType == Set.class){
				return verifyType(typeArgs[0]);
			}else{
				throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
			}
		}else if(type instanceof Class){
			Class<?> clazz = (Class<?>)type;
			if(null !=clazz.getDeclaringClass() && !Modifier.isStatic(clazz.getModifiers())){
				// 不允许为非静态成员类
				logger.error("unsupport not static member class {}",clazz);
				return false;
			}
			if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
				logger.error("unsupport primitive type {}",clazz);
				return false;
			}
			if(isThriftStruct(clazz)){
				knownTypes.add(clazz);
				thriftTypes.add(makeThriftStructDecorator(clazz));
				return true;
			}
			// 枚举类型
			if(Enum.class.isAssignableFrom(clazz)){
				knownTypes.add(clazz);
				decorateTypes.put(clazz,makeThriftStructDecorator(clazz));
				return true;
			}
			// 控制只递归一层
			if(clazz.isArray()){
				if(!clazz.getComponentType().isArray()){
					return verifyType(clazz.getComponentType());
				}else{
					logger.error("unsupport multi dimension array {}",clazz.toString());
					return false;
				}
			}
			if(BaseThriftUtils.isException(clazz)){
				return verifyException(clazz);
			}
			if(Object.class == clazz){
				logger.error("unsupport not type Object.class");
				return false;
			}
			String pkg = clazz.getPackage().getName();
			if(pkg.startsWith("java.") || pkg.startsWith("javax.")){
				logger.error("not allow type {}",type);
				return false;
			}
			return verifyStruct(clazz);
		}
		throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
	}
	private final Predicate<PropertyDescriptor> expFieldfilter= new Predicate<PropertyDescriptor>(){
		@Override
		public boolean apply(PropertyDescriptor input) {
			return input.getReadMethod().getDeclaringClass()!=Throwable.class;
		}};
	private boolean verifyException(Class<?> clazz){
		if(!BaseThriftUtils.isException(clazz)){
			return false;
		}
		if(!verifyFields(clazz)){
			return false;
		}
		if(null == BaseThriftUtils.getConstructor(clazz) 
			&& null == BaseThriftUtils.getConstructor(clazz,String.class)){
			// 没有默认构造方法也没有String参数构造方法返回false
			logger.error("not found default constructor or consturctor with String.class argument for {}",clazz.getName());
			return false;
		}
		knownTypes.add(clazz);
		decorateTypes.put(clazz,makeThriftStructDecorator(clazz));
		return true;
	}
	private boolean verifyStruct(Class<?> clazz){
		if(clazz.getTypeParameters().length > 0){
			logger.error("unsupport generic class {}",clazz.getName());
			return false;
		}
		try {
			@SuppressWarnings("unused")
			Constructor<?> ctor = clazz.getConstructor();
		} catch (NoSuchMethodException e) {
			// 没有默认构造方法返回false
			logger.error("not found default consturctor for {}",clazz.getName());
			return false;
		} 
		if(!verifyFields(clazz)){
			return false;
		}
		knownTypes.add(clazz);
		decorateTypes.put(clazz,makeThriftStructDecorator(clazz));
		return true;
	}
	private boolean verifyFields(Class<?> clazz){
		Map<String, PropertyDescriptor> fields = getFields(clazz);
		for(PropertyDescriptor descriptor:fields.values()){
			if(!verifyType(resolveType(clazz,descriptor.getReadMethod().getGenericReturnType()))){
				logger.warn("invalid type for {} in {}",descriptor.getName(),clazz.getName());
				return false;
			}
		}
		return true;
	}
	public Map<String, PropertyDescriptor> getFields(Class<?> clazz,Predicate<PropertyDescriptor>filter,boolean lenient){
		Map<String, PropertyDescriptor> fields = BEAN_SUPPORT.getProperties(clazz, 3,lenient);
		fields.values().forEach(p->{
			try {
				/**
				 * 如果方法是桥接方法，则使用原始方法，
				 * 可以通过readMethod的返回类型获取字段正确的泛型类型
				 * 比如：
				 * T为父类ClasA的泛型参数，子类ClassB将T实例化为String，
				 * ClassB重写了父类的 List<T> getList()方法，即 List<String> getList()，
				 * ClassB中桥接方法为 List getList(), 则getList()方法返回的泛型类型为Object
				 * 所以需要使用原始方法才能正确获取泛型类型 List<String>
				 */
				java.lang.reflect.Method readMethod = p.getReadMethod();
				java.lang.reflect.Method originalReadMethod = null == readMethod ? null : findBridgedMethod(readMethod);
				if(null != originalReadMethod && !originalReadMethod.equals(readMethod)){
					p.setReadMethod(originalReadMethod);
				}
				java.lang.reflect.Method writeMethod = p.getWriteMethod();
				java.lang.reflect.Method originalWriteMethod = null == writeMethod ? null : findBridgedMethod(writeMethod);
				if(null != originalWriteMethod && !originalWriteMethod.equals(writeMethod)){
					p.setWriteMethod(originalWriteMethod);
				}
			} catch (IntrospectionException e) {
				// DO NOTHING
			}
		});
		Predicate<PropertyDescriptor> f = checkNotNull(filter,"filter is null");
		if(BaseThriftUtils.isException(clazz) && (f != expFieldfilter)){
			f = Predicates.and(f, expFieldfilter);
		}
		return Maps.filterValues(fields, f);
	}
	public Map<String, PropertyDescriptor> getFields(Class<?> clazz,Predicate<PropertyDescriptor>filter){
		return getFields(clazz, filter, false);
	}
	public Map<String, PropertyDescriptor> getFields(Class<?> clazz){
		return getFields(clazz,Predicates.<PropertyDescriptor>alwaysTrue());
	}
	private boolean isDecoratorType(Type type){
		return this.decorateTypes.containsKey(type);
	}
	public List<ThriftStructDecorator> getDecorateTypes() {
		return ImmutableList.copyOf(decorateTypes.values());
	}
	/**
	 * @return 返回所有有 {@link com.facebook.swift.codec.ThriftStruct} 注释的类型
	 */
	public List<ThriftStructDecorator> getThriftTypes() {
		return ImmutableList.copyOf(thriftTypes);
	}
	/**
	 * 将指定的类型转为thrift支持的类型
	 * @param type
	 */
	@AoWeakCacheable
	public String toThriftType(Type type){
		checkArgument(null != type,"type is null");
		if(BaseThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type)){
			return parent.getTypeName(type);
		}
		if(BaseThriftUtils.CAST_TYPES.containsKey(type)){
			return parent.getTypeName(BaseThriftUtils.CAST_TYPES.get(type));
		}
		if(this.isDecoratorType(type)){
			// 枚举类型
			if(Enum.class.isAssignableFrom((Class<?>) type)){
				return parent.getTypeName(type);
			}
			return toDecoratorType(type);
		}
		if( type instanceof ParameterizedType){
			ParameterizedType paramType = (ParameterizedType)type;
			Type rawType = paramType.getRawType();
			Type[] typeArgs = paramType.getActualTypeArguments();
			if(rawType == Map.class){
				return String.format("Map<%s,%s>",toThriftType(typeArgs[0]), toThriftType(typeArgs[1]));
			}else if (rawType == List.class){
				return String.format("List<%s>",toThriftType(typeArgs[0]));
			}else if(rawType == Set.class){
				return String.format("Set<%s>",toThriftType(typeArgs[0]));
			}else{
				throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
			}
		}else if(type instanceof Class){
			Class<?> clazz = (Class<?>)type;
			if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
				throw new IllegalArgumentException(String.format("not allow type %s", clazz.toString()));
			}
			if(isThriftStruct(clazz)){
				return parent.getTypeName(clazz);
			}

			// 控制只递归一层
			if(clazz.isArray() ){
				Class<?> conmponentType = clazz.getComponentType();
				if(!conmponentType.isArray()){
					return String.format("java.util.List<%s>",toThriftType(Primitives.wrap(conmponentType)));
				}else{
					throw new IllegalArgumentException("unsupported type multi dimension array");
				}
			}
		}
		throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
	}
	/**
	 * 将指定的类型转为thrift支持的类型(递归)
	 * @param type
	 * @param toDecorator 为true时要求获取decorator
	 * @param sameIfThriftStruct 为true时,如果type为thrift struct则返回type 
	 * @param sameIfEnum 为true时,如果type为枚举类型则返回type 
	 * @param extKnownTypes 扩展已知类型
	 */
	private String toClientThriftType0(Type type,boolean toDecorator, boolean sameIfThriftStruct, boolean sameIfEnum, Map<Class<?>,Class<?>> extKnownTypes){
		checkArgument(null != type,"type is null");
		if(extKnownTypes != null && extKnownTypes.containsKey(type)){
			return parent.getTypeName(extKnownTypes.get(type));
		}
		if(BaseThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type) || type == Void.class ){
			return parent.getTypeName(type);
		}
		if(BaseThriftUtils.CAST_TYPES.containsKey(type)){
			return parent.getTypeName(BaseThriftUtils.CAST_TYPES.get(type));
		}
		if(sameIfThriftStruct && isThriftStruct(type)){
			return parent.getTypeName(type);
		}
		if(sameIfEnum && type instanceof Class<?> && ((Class<?>)type).isEnum()){
			return parent.getTypeName(type);
		}
		if(toDecorator && this.isDecoratorType(type) && !((Class<?>)type).isEnum()){
			return toDecoratorType(type);
		}
		if( type instanceof ParameterizedType){
			ParameterizedType paramType = (ParameterizedType)type;
			Type rawType = paramType.getRawType();
			Type[] typeArgs = paramType.getActualTypeArguments();
			if(rawType == Map.class){
				return String.format("Map<%s,%s>",toClientThriftType0(typeArgs[0],toDecorator, sameIfThriftStruct, sameIfEnum, extKnownTypes), toClientThriftType0(typeArgs[1],toDecorator, sameIfThriftStruct, sameIfEnum, extKnownTypes));
			}else if (rawType == List.class){
				return String.format("List<%s>",toClientThriftType0(typeArgs[0],toDecorator, sameIfThriftStruct, sameIfEnum, extKnownTypes));
			}else if(rawType == Set.class){
				return String.format("Set<%s>",toClientThriftType0(typeArgs[0],toDecorator, sameIfThriftStruct, sameIfEnum, extKnownTypes));
			}else{
				throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
			}
		}else if(type instanceof Class){
			Class<?> clazz = (Class<?>)type;
			if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)){
				throw new IllegalArgumentException(String.format("not allow type %s", clazz.toString()));
			}
			if(isThriftStruct(clazz) || clazz.isEnum() || this.isDecoratorType(clazz)){
				return ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage() + "." + clazz.getSimpleName();
			}
			// 控制只递归一层
			if(clazz.isArray() ){
				Class<?> conmponentType = clazz.getComponentType();
				if(!conmponentType.isArray()){
					return String.format("java.util.List<%s>",toClientThriftType0(Primitives.wrap(conmponentType),toDecorator, sameIfThriftStruct, sameIfEnum, extKnownTypes));
				}else{
					throw new IllegalArgumentException("unsupported type multi dimension array");
				}
			}
		}
		throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
	}
	
	private static final Map<Class<?>,Class<?>> thriftExtKnowntypes = ImmutableMap.<Class<?>,Class<?>>of(
			ByteBuffer.class,byte[].class,
			byte[].class,byte[].class);
	/**
	 * 将指定的类型转为thrift支持的类型
	 * @param type
	 */
	@AoWeakCacheable
	public String toClientThriftType(Type type){
		checkArgument(null != type,"type is null");
		return toClientThriftType0(type, false, false, false, thriftExtKnowntypes);
	}
	
	/**
	 * 返回指定类型在thrift client对应的装饰类名
	 * @param type
	 */
	@AoWeakCacheable
	public String toThriftDecoratorType(Type type){
		checkArgument(null != type,"type is null");
		return toClientThriftType0(type, true, true, true, thriftExtKnowntypes);
	}
	public boolean sameWithClientThriftType(Type type){
		String s1 = parent.getTypeName(type);
		String s2 = toClientThriftType(type);
		return Objects.equals(s1, s2);
	}
	public boolean sameWithThriftDecoratorType(Type type){
		String s1 = parent.getTypeName(type);
		String s2 = toThriftDecoratorType(type);
		return Objects.equals(s1, s2);
	}
	private static final Map<Class<?>,Class<?>> thriftyExtKnowntypes = ImmutableMap.<Class<?>,Class<?>>of(
			ByteBuffer.class,ByteString.class,
			byte[].class,ByteString.class,
			ByteString.class,ByteString.class);
	/**
	 * 将指定的类型转为Microsoft/thrifty支持的类型
	 * @param type
	 */
	@AoWeakCacheable
	public String toClientThriftyType(Type type){
		checkArgument(null != type,"type is null");
		if( type instanceof ParameterizedType){
			ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl((ParameterizedType) type);
			parameterizedType = (ParameterizedTypeImpl) parameterizedType.transform(ByteBuffer.class, ByteString.class);
			parameterizedType = (ParameterizedTypeImpl) parameterizedType.transform(byte[].class, ByteString.class);
			type = parameterizedType;
		}
		return toClientThriftType0(type, false, false, false, thriftyExtKnowntypes);
	}
	public boolean isClientThriftType(Type type){
		checkArgument(null != type,"type is null");
		if(type == ByteBuffer.class){
			return false;
		}		
		if(BaseThriftUtils.THRIFT_BUILTIN_KNOWNTYPES.contains(type) || type == byte[].class || type == Void.class ){
			return true;
		}
		if(BaseThriftUtils.CAST_TYPES.containsKey(type)){
			return false;
		}
		if( type instanceof ParameterizedType){
			ParameterizedType paramType = (ParameterizedType)type;
			Type rawType = paramType.getRawType();
			Type[] typeArgs = paramType.getActualTypeArguments();
			if(rawType == Map.class){
				return isClientThriftType(typeArgs[0]) && isClientThriftType(typeArgs[1]);
			}else if (rawType == List.class){
				return isClientThriftType(typeArgs[0]);
			}else if(rawType == Set.class){
				return isClientThriftType(typeArgs[0]);
			}else{
				throw new IllegalArgumentException(String.format("not allow parameterized type %s", type.toString()));
			}
		}else if(type instanceof Class){
			return false;
		}
		throw new IllegalArgumentException(String.format("not allow type %s", type.toString()));
	}
	private String toDecoratorType(Type type){
		if(this.isDecoratorType(type)){
			ThriftStructDecorator decorator = decorateTypes.get(type);
			if(decorator.getDecoratorPackage().equals(getParentPackage())
					&& !parent.getImportedList().containsValue(type)){
				return ((Class<?>)type).getSimpleName();
			}else{
				return decorator.getDecoratorClassName();
			}
		}
		return parent.getTypeName(type);
	}
	/**
	 * 返回指定类型在thrifty client对应的装饰类名，如果非decorator,则调用{@link #toClientThriftType0(Type, boolean, boolean, boolean, Map)}
	 * @param type
	 */
	@AoWeakCacheable
	public String toThriftyDecoratorType(Type type){
		if(type == ByteBuffer.class || type == byte[].class){
			return parent.getTypeName(byte[].class);
		}		
		return toClientThriftType0(type,true, true, true, null);
	}
	
	public boolean sameWithClientThriftyType(Type type){
		String s1 = parent.getTypeName(type);
		String s2 = toClientThriftyType(type);
		return Objects.equals(s1, s2);
	}
	public boolean sameWithThriftyDecoratorType(Type type){
		String s1 = parent.getTypeName(type);
		String s2 = toThriftyDecoratorType(type);
		return Objects.equals(s1, s2);
	}
	public void checkType(Type type){
		if(!verifyType(type)){
			throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s",
					type.toString()
					));
		}
	}
	/**
	 * 确保方法的参数和返回类型符合要求
	 * @param method
	 */
	public void checkType(Method method){
		checkParameter(method);
		checkReturnType(method);
		checkThrows(method);
	}
	
	/**
	 * 验证参数类型
	 * @param method
	 */
	public void checkParameter(Method method){
		for (Parameter param : method.getParameters()) {
			Type genericType = resolveType(parent.getBaseClass(),param.getGenericType());
			if(!verifyType(genericType)){
				throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s of parameter %s in %s",
						genericType.toString(),
						param.name,
						method.getName()
						));
			}
		}
	}
	/**
	 * 验证返回类型
	 * @param method
	 */
	public void checkReturnType(Method method){
		Type returnType = resolveType(parent.getBaseClass(),method.getGenericReturnType());
		if(!verifyType(returnType)){
			throw new IllegalArgumentException(String.format("UNSUPPORTED TYPE %s of return type for %s",
					returnType.toString(),
					method.getName()
					));
		}
	}
	public void checkThrows(Method method){
		for(Type exp:method.getGenericExceptionTypes()){
			if(!verifyType(resolveType(parent.getBaseClass(),exp))){
				throw new IllegalArgumentException(String.format("UNSUPPORTED EXCEPTION TYPE %s in %s",
						exp.toString(),
						method.getName()
						));
			}
		};
		
	}
	
	/**
	 * 确保{@code clazz}不是泛型类
	 * @param clazz
	 */
	public static void checkNotGeneric(Class<?> clazz){
		if(clazz.getTypeParameters().length > 0){
			throw new IllegalArgumentException(String.format("%s must not be a generic class", clazz.getName()));
		}
	}
	/**
	 * 确保{@code method}不是泛型方法
	 * @param method
	 */
	public static void checkNotGeneric(java.lang.reflect.Method method){
		if(method.getTypeParameters().length > 0){
			throw new IllegalArgumentException(String.format("%s must not be a generic method", method.getName()));
		}
	}
	/**
	 * 确保{@code method}不是泛型方法
	 * @param method
	 */
	public static void checkNotGeneric(Method method){
		if(method.getTypeParameters().length > 0){
			throw new IllegalArgumentException(String.format("%s must not be a generic method", method.getName()));
		}
	}

	private String getParentPackage(){
		if(parent instanceof ThriftStructDecorator){
			return ((ThriftStructDecorator)parent).getDecoratorPackage();
		}else if(parent instanceof ThriftServiceDecorator){
			return ((ThriftServiceDecorator<?>)parent).getGeneratePackage();
		}
		return null;
	}
	public void addReferTypes(Type type){
		traverseTypes(type,new Action(){
			@Override
			public void doClass(Class<?> clazz) {
				referTypes.add(clazz);
				parent.addImportedClass(clazz);
				if(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType().castReferType
						&& CAST_TYPES.containsKey(clazz)){
					parent.addImportedClass(CAST_TYPES.get(clazz));
					referTypes.add(CAST_TYPES.get(clazz));
				}
			}});
	}
	public void addReferTypes(Method method){
		for (Parameter param : method.getParameters()) {
			addReferTypes(param.getGenericType());
		}
		addReferTypes(method.getGenericReturnType());
		for(Type exp:method.getGenericExceptionTypes()){
			addReferTypes(exp);
		}
	}
	public boolean needTransformer(){
		return Iterators.tryFind(referTypes.iterator(), new Predicate<Class<?>>(){
			@Override
			public boolean apply(Class<?> input) {
				return BaseThriftUtils.needTransformer(input);
			}}).isPresent();
	}
	public List<Class<?>> getTypesWithDecorator(){
		return Lists.transform(getDecorateTypes(),new Function<ThriftStructDecorator,Class<?>>(){
			@Override
			public Class<?> apply(ThriftStructDecorator input) {
				return input.getBaseClass();
			}});
	}
	public List<Class<?>> getReferExceptions(){
		Iterable<Class<?>> iterable = Iterables.filter(referTypes, new Predicate<Class<?>>(){
			@Override
			public boolean apply(Class<?> input) {
				return Exception.class.isAssignableFrom(input);
			}});
		return Lists.newArrayList(iterable);
	}
	/**
	 * 创建{@link ThriftStructDecorator}实例<br>
	 * 如果{@code structType}有循环引用则抛出异常
	 * @param structType
	 */
	static ThriftStructDecorator makeThriftStructDecorator(Class<?> structType){
        checkNotNull(structType, "structType is null");
        Deque<Class<?>> stack = tlsStack.get();
        if (stack.contains(structType)) {
            String path = Joiner.on("->").join(Iterables.transform(Iterables.concat(stack, ImmutableList.of(structType)), new Function<Class<?>, Object>()
            {
                @Override
                public Object apply(Class<?> input)
                {
                    return TypeToken.of(input).getRawType().getName();
                }
            }));
            throw new IllegalArgumentException("Circular references are not allowed: " + path);
        }
        stack.push(structType);
        try {
            return new ThriftStructDecorator(structType);
        }
        finally {
        	Class<?> top = stack.pop();
            checkState(structType.equals(top),
                    "ThriftCatalog circularity detection stack is corrupt: expected %s, but got %s",
                    structType,
                    top);
        }
	}
}
